package ops

import (
	"context"
	"github.com/go-xorm/xorm"
	helmClient "github.com/mittwald/go-helm-client"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"gluenet/pkg/log/v1"
	"gluenet/pkg/ops/impl/es"
	helm2 "gluenet/pkg/ops/impl/helm"
	"gluenet/pkg/ops/impl/influxdb"
	"gluenet/pkg/ops/impl/jaeger"
	"gluenet/pkg/ops/impl/mysql"
	"gluenet/pkg/ops/impl/prom"
)

const (
	SqlDataBase         = "mysql"
	SqlDataBaseInfluxdb = "data"
	EsSpan              = "es_trace"
	EsLog               = "vector_log"
	K8sLog              = "k8s_log"
	LogsInfo            = "logsInfo"
	Logs                = "logs"
)

type DefaultDB struct {
	ctx   context.Context
	label string

	ip    string
	port  string
	mysql *xorm.Engine

	mysql2 *xorm.Engine

	url      string
	urlLog   string
	token    string
	tokenLog string
	influxdb *influxdb.Client

	esUrl string
	es    *es.Es

	helmUrl   string
	helmRepo  string
	helmCache string
	helm      helmClient.Client

	jaegerUrl string
	jaeger    *jaeger.Jaeger

	promUrl string
	prom    *prom.Client

	promUrl2 string
	prom2    *prom.Client
}

func NewDB(ctx context.Context, label string) *DefaultDB {
	return &DefaultDB{
		label: label,
		ctx:   ctx,
	}
}

func (r *DefaultDB) InitFlag(flags *pflag.FlagSet) {
	flags.String(r.label+".mysql.ip", "39.101.140.145", "")
	flags.String(r.label+".mysql.port", "30005", "")

	flags.String(r.label+".influxdb.url", "http://39.101.140.145:30050", "")
	flags.String(r.label+".influxdb.url.log", "http://39.101.140.145:30060", "")
	flags.String(r.label+".influxdb.token", "ADxqhX1olnfhbZSayVw_UzWlaxrb7WyoqB-RRF4szOBZRMAgLRJq1uYrfQ5DvqPnIuvmbylf_QITXOOfpvHqRg==", "")
	flags.String(r.label+".influxdb.token.log", "EankS09dQvpMPsKI0pSNxMONMd8f6d7MAWn4Xg_ZDUVrqbp_6-8EkpSb1LamF8uuPpt8c9SiENrAM4rTQvnvYw==", "")

	flags.String(r.label+".es.url", "http://39.101.140.145:30023", "")

	flags.String(r.label+".helm.url", "http://39.101.140.145:30028", "")
	flags.String(r.label+".helm.repo", "gluerepo", "")
	flags.String(r.label+".helm.cache", "../../../build/", "")

	flags.String(r.label+".jaeger.url", "39.101.140.145:30025", "")

	flags.String(r.label+".prometheus.remote.write", "http://39.101.140.145:30051/write", "")
	flags.String(r.label+".prometheus.remote.write.v2", "http://39.101.140.145:30009/api/v1/write", "")
}

func (r *DefaultDB) InitViper(viper *viper.Viper) {
	r.ip = viper.GetString(r.label + ".mysql.ip")
	r.port = viper.GetString(r.label + ".mysql.port")

	r.url = viper.GetString(r.label + ".influxdb.url")
	r.urlLog = viper.GetString(r.label + ".influxdb.url.log")
	r.token = viper.GetString(r.label + ".influxdb.token")
	r.tokenLog = viper.GetString(r.label + ".influxdb.token.log")

	r.esUrl = viper.GetString(r.label + ".es.url")

	r.helmUrl = viper.GetString(r.label + ".helm.url")
	r.helmRepo = viper.GetString(r.label + ".helm.repo")
	r.helmCache = viper.GetString(r.label + ".helm.cache")

	r.jaegerUrl = viper.GetString(r.label + ".jaeger.url")

	r.promUrl = viper.GetString(r.label + ".prometheus.remote.write")
	r.promUrl2 = viper.GetString(r.label + ".prometheus.remote.write.v2")

	model := viper.GetString("mode")
	if model == "prod" {
		r.ip = viper.GetString("MYSQL_IP")
		r.port = viper.GetString("MYSQL_PORT")
		r.url = viper.GetString("INFLUXDB_URL")
		r.urlLog = viper.GetString("INFLUXDB_URL_LOG")
		r.token = viper.GetString("INFLUXDB_TOKEN")
		r.tokenLog = viper.GetString("INFLUXDB_TOKEN_LOG")
		r.esUrl = viper.GetString("ES_URL")

		r.helmCache = viper.GetString("HELM_CACHE")
		r.helmRepo = viper.GetString("HELM_REPO")
		r.helmUrl = viper.GetString("HELM_URL")

		r.jaegerUrl = viper.GetString("JAEGER_URL")

		r.promUrl = viper.GetString("PROMETHEUS_URL")
		r.promUrl2 = viper.GetString("PROMETHEUS_URL_V2")

		v1.NewLogger("prod").Debugf("%v %v %v %v %v %v %v %v %v %v", r.ip,
			r.port, r.url, r.token, r.esUrl, r.helmUrl, r.helmRepo, r.helmUrl, r.jaegerUrl, r.promUrl)
	}

}

func (r *DefaultDB) Connect() error {
	db, err := mysql.OpenMySql(r.ip, r.port)
	if err != nil {
		return err
	}
	r.mysql = db

	db2, err := mysql.OpenMySql2(r.ip, r.port)
	if err != nil {
		return err
	}
	r.mysql2 = db2

	idb, err := influxdb.OpenInfluxdb(r.url, r.token, r.urlLog, r.tokenLog)
	if err != nil {
		return err
	}
	r.influxdb = idb
	e, err := es.NewEs(r.ctx, r.esUrl)
	if err != nil {
		return err
	}
	r.es = e

	h, err := helm2.OpenRepo(r.helmUrl, r.helmRepo, r.helmCache)
	if err != nil {
		return err
	}
	r.helm = h

	j := jaeger.OpenJaeger(r.ctx, r.jaegerUrl)
	r.jaeger = j

	p, err := prom.OpenProm(r.promUrl)
	if err != nil {
		return err
	}
	r.prom = p
	p2, err := prom.OpenProm(r.promUrl2)
	if err != nil {
		return err
	}
	r.prom2 = p2
	return err
}

func (r *DefaultDB) Close() error {
	//r.influxdb.Close()
	return r.mysql.Close()
}

//func (r *DefaultDB) SqlXorm() *xorm.Engine {
//	return r.mysql
//}

func (r *DefaultDB) HelmCharts(name string) ([]*helm2.Result, error) {
	idx, err := helm2.BuildIndex(r.helmCache+".helmrepo", r.helmCache+".helmcache")
	if err != nil {
		return nil, err
	}

	var res []*helm2.Result

	if len(name) == 0 {
		// list all
		res = idx.All()
	} else {
		// get some
		res, err = idx.Search(name, 25, false)
		if err != nil {
			return nil, err
		}
	}
	helm2.SortScore(res)

	return res, nil
}

//func (r *DefaultDB) SqlDB() interface{} {
//	//TODO implement me
//	return r.mysql.DB()
//}

func (r *DefaultDB) MetaDB() *xorm.Engine {
	//TODO implement me
	return r.mysql2
}

func (r *DefaultDB) LogsDB() *influxdb.Client {
	return r.influxdb
}

func (r *DefaultDB) MetricDB() *influxdb.Client {
	return r.influxdb
}

//func (r *DefaultDB) NoSqlDB() interface{} {
//	//TODO implement me
//	panic("implement me")
//}

//func (r *DefaultDB) CreateTable(dataBase string, data interface{}) error {
//	switch dataBase {
//	case SqlDataBase:
//		return r.mysql.CreateTables(data)
//	case SqlDataBaseInfluxdb:
//		return nil
//	default:
//		return errors.New("unknown data base")
//	}
//}
//
//func (r *DefaultDB) DropTable(dataBase string, data interface{}) error {
//	switch dataBase {
//	case SqlDataBase:
//		return r.mysql.DropTables(data)
//	case SqlDataBaseInfluxdb:
//		return nil
//	default:
//		return errors.New("unknown data base")
//	}
//}
//
//func (r *DefaultDB) QueryOne(sql string, dataBase string, retData interface{}) error {
//	switch dataBase {
//	case SqlDataBase:
//		return r.mysql.SQL(sql).Find(retData)
//	case SqlDataBaseInfluxdb:
//		return nil
//	default:
//		return errors.New("unknown data base")
//	}
//}
//
//func (r *DefaultDB) QueryList(sql string, dataBase string, retData interface{}) error {
//	switch dataBase {
//	case SqlDataBase:
//		return r.mysql.SQL(sql).Find(retData)
//	case SqlDataBaseInfluxdb:
//		return r.influxdb.GetList(r.ctx, sql, retData)
//	case LogsInfo:
//		return r.influxdb.GetLogsInfo(r.ctx, sql, retData)
//	case Logs:
//		return r.influxdb.GetLogs(r.ctx, sql, retData)
//	default:
//		return errors.New("unknown data base")
//	}
//}
//
//func (r *DefaultDB) UpdateSql(sql string, dataBase string, retData interface{}) (int64, error) {
//	switch dataBase {
//	case SqlDataBase:
//		return r.mysql.SQL(sql).Update(retData)
//	case SqlDataBaseInfluxdb:
//		return 0, nil
//	default:
//		return 0, errors.New("unknown data base")
//	}
//}

//func (r *DefaultDB) Update(dataBase string, data interface{}) (int64, error) {
//	switch dataBase {
//	case SqlDataBase:
//		return r.mysql.Update(data)
//	case SqlDataBaseInfluxdb:
//		return 0, nil
//	default:
//		return 0, errors.New("unknown data base")
//	}
//}

//func (r *DefaultDB) UpdateByLabel(dataBase string, tableName, label, guid string, data interface{}) (int64, error) {
//	switch dataBase {
//	case SqlDataBase:
//		return r.mysql.Table(tableName).Where("label = ? and guid = ?", label).Update(data)
//	case SqlDataBaseInfluxdb:
//		return 0, nil
//	default:
//		return 0, errors.New("unknown data base")
//	}
//}

//func (r *DefaultDB) Delete(dataBase string, data interface{}) (int64, error) {
//	switch dataBase {
//	case SqlDataBase:
//		return r.mysql.Delete(data)
//	case SqlDataBaseInfluxdb:
//		return 0, nil
//	default:
//		return 0, errors.New("unknown data base")
//	}
//}
//
//func (r *DefaultDB) InsertOne(dataBase string, data interface{}) (int64, error) {
//	switch dataBase {
//	case SqlDataBase:
//		return r.mysql.InsertOne(data)
//	case SqlDataBaseInfluxdb:
//		return 0, nil
//	case EsSpan:
//		bts, ok := data.([]byte)
//		if !ok {
//			return 0, errors.New("data must be byte")
//		}
//		return 0, r.es.WriteSpan(bts)
//	case EsLog:
//		d, ok := data.(map[string]interface{})
//		if !ok {
//			return 0, errors.New("data must be map[string]interface")
//		}
//		return 0, r.es.WriteLog(r.ctx, d["guid"].(string), data)
//	case K8sLog:
//		return 0, r.influxdb.Putk8sLogs(data)
//	default:
//		return 0, errors.New("unknown data base")
//	}
//}

//func (r *DefaultDB) InsertList(dataBase string, data interface{}) (int64, error) {
//	switch dataBase {
//	case SqlDataBase:
//		return r.mysql.Insert(data)
//	case SqlDataBaseInfluxdb:
//		dataS, ok := data.([]interface{})
//		if !ok {
//			return 0, errors.New("data must be list")
//		}
//		err := r.influxdb.PutList(dataS)
//		return 0, err
//	default:
//		return 0, errors.New("unknown data base")
//	}
//}

//
//func (r *DefaultDB) Transaction() GlueDBSession {
//	return &Transaction{
//		Session: r.mysql.NewSession(),
//	}
//}

//func (r *DefaultDB) GetStatus(sql string, retData interface{}) error {
//	return r.influxdb.GetStatus(r.ctx, sql, retData)
//}

func (r *DefaultDB) MetricsQuerySQL(sql string, retData interface{}) error {
	return r.influxdb.GetMetrics(r.ctx, sql, retData)
}

//
//func (r *DefaultDB) GetK8sNodes(sql string, retData interface{}) error {
//	return r.influxdb.GetK8sNodes(r.ctx, sql, retData)
//}
//
//func (r *DefaultDB) GetK8sPodContainers(sql string, retData interface{}) error {
//	return r.influxdb.GetK8sPodContainers(r.ctx, sql, retData)
//}
//
//func (r *DefaultDB) GetK8sContainerInfo(sql string, retData interface{}) error {
//	return r.influxdb.GetK8sContainerInfo(r.ctx, sql, retData)
//}

func (r *DefaultDB) GetTracesK8s(guid string, start, end, limit int64, retData interface{}) error {
	return r.jaeger.GetTracesK8s(guid, start, end, limit, retData)
}

func (r *DefaultDB) GetTracesId(id string, retData interface{}) error {
	return r.jaeger.GetTracesId(id, retData)
}

func (r *DefaultDB) GetTracesGluenets(guid string, containers []string, start, end, limit int64, retData interface{}) error {
	return r.jaeger.GetTracesGluenets(guid, containers, start, end, limit, retData)
}

func (r *DefaultDB) GetLogs(sql string, retData interface{}) error {
	return r.influxdb.GetLogs(r.ctx, sql, retData)
}
